﻿using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Threading;
using Pixster.Slideshow.BusinessObjects;
using System.Windows.Media.Imaging;
using Pixster.Slideshow.ShaderEffects;

namespace Pixster.Slideshow
{
    /// <summary>
    /// Handle the main logic for the slide show.
    /// </summary>
    /// 
    [TemplatePart(Name = "PartHost", Type = typeof(Grid))]
    public class SlideShowControl : Control
    {
        /// <summary>
        /// Current slide show photo.
        /// </summary>
        private PhotoHost _currentPhotoHost;

        /// <summary>
        /// Temporarilly hosts the old slide as well as the next slide.
        /// </summary>
        private PhotoHost _tempPhotoHost;

        /// <summary>
        /// Border that hosts the photo controls.
        /// </summary>
        private Border _photoHost;

        /// <summary>
        /// Is the slide show paused (stopped).
        /// </summary>
        private bool _isPaused;

        /// <summary>
        /// Used to play the slide show.
        /// </summary>
        readonly DispatcherTimer _slideShowTimer;

        /// <summary>
        /// Dependency property for the slide show.
        /// </summary>
        public static readonly DependencyProperty PhotoSlideShowProperty =
            DependencyProperty.Register("PhotoSlideShow", typeof(PhotoSlideShow), typeof(SlideShowControl),
                new PropertyMetadata(OnPhotoSlideShowChanged));

        public event EventHandler PausedChanged;

        public SlideShowControl()
        {
            this._currentPhotoHost = new PhotoHost { Style = Application.Current.Resources["PhotoHost"] as Style };
            this._tempPhotoHost = new PhotoHost { Style = Application.Current.Resources["PhotoHost"] as Style };

            this._slideShowTimer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(5) };
            this._slideShowTimer.Tick += this.OnTimerTick;
            this._slideShowTimer.Stop();
        }

        /// <summary>
        /// Gets, sets the photo slide show.
        /// </summary>
        public PhotoSlideShow PhotoSlideShow
        {
            get
            {
                return this.GetValue(PhotoSlideShowProperty) as PhotoSlideShow;
            }
            set
            {
                this.SetValue(PhotoSlideShowProperty, value);
            }
        }

        public override void OnApplyTemplate()
        {
            base.OnApplyTemplate();

            this._photoHost = this.GetTemplateChild("PART_Host") as Border;
            if (this._photoHost == null) return;

            this._photoHost.Child = _currentPhotoHost;
            this._slideShowTimer.Start();
        }

        public bool IsPaused
        {
            get
            {
                return this._isPaused;
            }
            protected set
            {
                if (this.IsPaused == value)
                    return;

                this._isPaused = value;
                this.OnPausedChanged(EventArgs.Empty);
            }
        }

        public void StopSlideShow()
        {
            this._slideShowTimer.Stop();
            this.IsPaused = true;
        }

        public void PauseSlideShow()
        {
            this.StopSlideShow();
        }

        public void ResumeSlideShow()
        {
            this._slideShowTimer.Start();
            this.IsPaused = false;
        }

        public void NextSlide()
        {
            if (this.IsPaused == false)
                this.StopSlideShow();

            this.PhotoSlideShow.MoveNext();
            this.ChangePhoto(false);
        }

        public void PreviousSlide()
        {
            if (this.IsPaused == false)
                this.StopSlideShow();

            this.PhotoSlideShow.MovePrevious();
            this.ChangePhoto(false);
        }

        protected virtual void OnPausedChanged(EventArgs e)
        {
            var temp = this.PausedChanged;
            if (temp != null)
                temp(this, e);
        }

        private static void OnPhotoSlideShowChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            var pss = e.NewValue as PhotoSlideShow;
            var ssc = sender as SlideShowControl;

            if (pss == null)
            {
                ssc._tempPhotoHost.Photo = null;
                ssc._currentPhotoHost.Photo = null;
                ssc._slideShowTimer.Stop();
            }
            else
            {
                ssc._currentPhotoHost.Photo = pss.CurrentPhoto;
                ssc._tempPhotoHost.Photo = pss.NextPhoto;
                if (ssc._photoHost != null)
                    ssc._slideShowTimer.Start();
            }
        }

        private void OnTimerTick(object sender, EventArgs e)
        {
            if (this.PhotoSlideShow != null)
                this.PhotoSlideShow.MoveNext();
            this.ChangePhoto(true);
        }

        private void ChangePhoto()
        {
            var temp = this._currentPhotoHost;
            this._currentPhotoHost = this._tempPhotoHost;

            this._tempPhotoHost = temp;
            this._currentPhotoHost.Width = Double.NaN;
            this._currentPhotoHost.Height = Double.NaN;
            if (_photoHost != null)
            {
                _photoHost.Child = _currentPhotoHost;
            }

            this._tempPhotoHost.Effect = null;
        }

        private void ChangePhoto(bool applyEffect)
        {
            if (this.PhotoSlideShow == null)
                return;

            if (applyEffect)
            {
                this.ChangePhoto();
                this.ApplyEffectAnimation();
            }
            else
            {
                this._currentPhotoHost.Photo = this.PhotoSlideShow.CurrentPhoto;
                this._tempPhotoHost.Photo = this.PhotoSlideShow.NextPhoto;
            }
        }

        private void ApplyEffectAnimation()
        {
            var effect = TransitionEffectsManager.Instance.CurrentEffect;

            var progressStoryboard = new Storyboard();
            var da = new DoubleAnimation();
            Storyboard.SetTarget(da, effect);
            Storyboard.SetTargetProperty(da, new PropertyPath(TransitionEffect.ProgressProperty));
            da.From = 0;
            da.To = 1;
            da.Duration = TimeSpan.FromSeconds(2.5);
            da.FillBehavior = FillBehavior.HoldEnd;
            da.Completed += this.OnEffectAnimationCompleted;
            progressStoryboard.Children.Add(da);
            progressStoryboard.Begin();

            var wb = new WriteableBitmap(this._tempPhotoHost, null);
            var imageBrush = new ImageBrush { ImageSource = wb };

            this._tempPhotoHost.Width = this._tempPhotoHost.ActualWidth;
            this._tempPhotoHost.Height = this._tempPhotoHost.ActualHeight;
            this._tempPhotoHost.Measure(new Size(this._tempPhotoHost.ActualWidth, this._tempPhotoHost.ActualHeight));
            this._tempPhotoHost.Arrange(new Rect(0, 0, this._tempPhotoHost.ActualWidth, this._tempPhotoHost.ActualHeight));

            effect.OldImage = imageBrush;
            this._currentPhotoHost.Effect = effect;
        }

        private void OnEffectAnimationCompleted(object sender, EventArgs e)
        {
            this._currentPhotoHost.Effect = null;
            if (this.PhotoSlideShow == null) return;

            this._tempPhotoHost.Photo = this.PhotoSlideShow.NextPhoto;
            TransitionEffectsManager.Instance.MoveNext();
        }
    }
}